home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2007 December
/
PCWKCD1207B.iso
/
Blogowanie poza sfera
/
Flock 0.9.1.3 stable
/
flock-0.9.1.3.en-US.win32.exe
/
flock
/
components
/
flockYoutubeService.js
< prev
next >
Wrap
Text File
|
2007-10-12
|
47KB
|
1,513 lines
// vim: ts=2 sw=2 expandtab cindent
//
// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2007
// http://flock.com
//
// This file may be used under the terms of of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// END FLOCK GPL
//
// Developer ID = 1aiV9CsTs3o
// Developer Secret = flock_is_open_source
const ENABLE_DEBUG = true; // switch to turn off slow debug code for production
function DEBUG(x) { if (ENABLE_DEBUG) debug("flockYouTubeService: "+x+"\n"); }
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
const YT_DEVID = "1aiV9CsTs3o";
const YT_DEVSECRET = "flock_is_open_source";
const YOUTUBE_CID = Components.ID('{DCB6A01E-7D4A-4C30-AB3D-9CC98C02F617}');
const YOUTUBE_CONTRACTID = '@flock.com/?photo-api-youtube;1';
const YOUTUBE_FAVICON = "http://www.youtube.com/favicon.ico";
const YOUTUBE_TITLE = "YouTube Web Service";
const SERVICE_ENABLED_PREF = "flock.service.youtube.enabled";
const CATEGORY_COMPONENT_NAME = "YouTube JS Component"
const CATEGORY_ENTRY_NAME = "youtube"
const FLOCK_PHOTO_CONTRACTID = '@flock.com/photo;1';
const FLOCK_PHOTOPERSON_CONTRACTID = '@flock.com/photo-person;1';
const FLOCK_PHOTO_ALBUM_CONTRACTID = '@flock.com/photo-album;1';
const PHOTOAPIMGR_CONTRACTID = "@flock.com/photo-api-manager;1?";
const OBS_TOPIC_XPCOMSHUTDOWN = "xpcom-shutdown";
var gCompTK;
function getCompTK() {
if (!gCompTK) {
gCompTK = Cc["@flock.com/singleton;1"]
.getService(Ci.flockISingleton)
.getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
.wrappedJSObject;
}
return gCompTK;
}
loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
var gTimers = []; // For use with the scheduler
// String defaults... may be updated later through Web Detective
var gStrings = {
"domains": "youtube.com",
"userlogin": "http://www.youtube.com/",
"userprofile": "http://www.youtube.com/profile?user=%accountid%",
"editprofile": "http://youtube.com/my_profile",
"inbox": "http://youtube.com/my_messages",
"subscriptions": "http://youtube.com/subscription_center"
};
/* JMC - Notes for refactoring
CTOR - Always addsObserver for flock-data-ready,
so that the service can be initialized with a proper faves-coop.
Updating actions at CTOR time is expensive
- do in polling instead?
- or on state change
*/
function youtubeVideo() {
}
youtubeVideo.prototype= {
id: "",
thumbnail: "",
webPageUrl: "",
midSizePhoto: "",
largeSizePhoto: "",
title: "",
username: "",
userid: "",
is_public: "true",
is_video: "true",
has_miniView: "true",
svcShortName: 'youtube',
buildTooltip: function( ) {
// do we have to use document from the window to ceate elements? -- ja
var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var win = wm.getMostRecentWindow('navigator:browser');
if (!win) return null;
var box = win.document.createElement('vbox');
box.setAttribute('style', 'max-width: 250px');
var title = win.document.createElement('label');
title.setAttribute('value', this.title );
title.setAttribute('crop', 'end');
box.appendChild(title);
if( this.length_seconds)
{
var lbl = win.document.createElement('label');
lbl.setAttribute('value', 'Length: ' + this.length_seconds + ' seconds');
box.appendChild(lbl);
}
if(this.rating_avg)
{
var lbl = win.document.createElement('label');
lbl.setAttribute('value', 'Rating: ' + this.rating_avg + ' (' + this.rating_count + ' times)');
box.appendChild(lbl);
}
if(this.view_count)
{
var lbl = win.document.createElement('label');
lbl.setAttribute('value', 'Views: ' + this.view_count );
box.appendChild(lbl);
}
if(!(this.length_seconds && this.rating_avg && this.view_count))
{
//if the tooltip does not these metadata -- show the author
var lbl = win.document.createElement('label');
lbl.setAttribute('value', this.username );
lbl.setAttribute('class', 'user');
box.appendChild(lbl);
}
var vbox = win.document.createElement('vbox');
var cbox = win.document.createElement('cbox');
var largeImg = win.document.createElement('image');
largeImg.setAttribute('src', this.midSizePhoto);
largeImg.setAttribute('style', 'margin-bottom: 2px;');
var spacer = win.document.createElement('spacer');
spacer.setAttribute('flex', '1');
cbox.appendChild(largeImg);
cbox.appendChild(spacer);
vbox.appendChild(cbox);
vbox.appendChild(box);
return vbox;
},
buildHTML: function ( ) {
var flashUrl = this.webPageUrl.replace(/\/\?v\=/,'/v/');
return '<object width="425" height="350">'
+ '<param name="movie" value="'+flashUrl+'"/>'
+ '<embed src="'+flashUrl+'" type="application/x-shockwave-flash" width="425" height="350"/></object>';
},
buildBBCode: function ( ) {
var video = this.webPageUrl.replace(/\/\?v\=/,'/watch?v=');
return '[youtube]' + video + '[/youtube]'
},
buildMiniPage: function ( ) {
var theurl = this.webPageUrl.replace(/\/\?v\=/,'/v/');
return '<html><head><title>' + this.title + ' (' + this.username + ')</title></head>' +
'<body><object width="425" height="350">' +
'<param name="movie" value="'+theurl+'"/>' +
'<center><embed src="'+theurl+'&autoplay=1" type="application/x-shockwave-flash" width="425" height="350"/></object></center></body></html>';
},
QueryInterface: function(iid) {
if (!iid.equals(Ci.nsISupports) &&
!iid.equals(Ci.flockIPhoto)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
}
};
youtubeVideo.prototype.__defineGetter__('metaData', function () {
var metaData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag2);
metaData.setPropertyAsAString("title", this.title);
metaData.setPropertyAsAString("length", this.length_seconds);
metaData.setPropertyAsAString("views", this.view_count);
metaData.setPropertyAsAString("rating", this.rating_avg);
metaData.QueryInterface(Ci.nsIPropertyBag);
return metaData;
})
// ================================================
// ========== BEGIN youtubeService class ==========
// ================================================
function youtubeService()
{
this.obs = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
this.obs.addObserver(this, OBS_TOPIC_XPCOMSHUTDOWN, false);
this.acUtils = Components.classes["@flock.com/account-utils;1"]
.getService(Components.interfaces.flockIAccountUtils);
this.status = Components.interfaces.flockIWebService.STATUS_UNKNOWN;
this.url = "http://www.youtube.com";
this.mIsInitialized = false;
this._ctk = {
interfaces: [
"nsISupports",
"nsISupportsCString",
"nsIClassInfo",
"nsIObserver",
"flockIWebService",
"flockIMediaWebService",
"flockISocialWebService",
"flockIManageableWebService",
"flockIPhotoAPI",
"flockIPollingService",
//"flockIPeopleActionController",
],
shortName: "youtube",
fullName: "YouTube",
description: YOUTUBE_TITLE,
favicon: YOUTUBE_FAVICON,
CID: YOUTUBE_CID,
contractID: YOUTUBE_CONTRACTID,
accountClass: youtubeAccount,
needPassword: false
};
this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
this._logger.init('youtube');
this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
this.init();
}
// BEGIN nsIObserver interface
youtubeService.prototype.observe =
function youtubeService_observe(subject, topic, state)
{
switch (topic) {
case OBS_TOPIC_XPCOMSHUTDOWN:
{
this.obs.removeObserver(this, OBS_TOPIC_XPCOMSHUTDOWN);
}; break;
}
}
// END nsIObserver interface
youtubeService.prototype.init =
function youtubeService_init()
{
DEBUG(".init()");
// Prevent re-entry
if (this.mIsInitialized) return;
this.mIsInitialized = true;
var evtID = this._profiler.profileEventStart("youtube-init");
this.prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
if ( this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
!this.prefService.getBoolPref(SERVICE_ENABLED_PREF) )
{
DEBUG("Pref "+SERVICE_ENABLED_PREF+" set to FALSE... not initializing.");
var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
catMgr.deleteCategoryEntry("wsm-startup", CATEGORY_COMPONENT_NAME, true);
catMgr.deleteCategoryEntry("flockIPhotoAPI", CATEGORY_ENTRY_NAME, true);
catMgr.deleteCategoryEntry("flockWebService", CATEGORY_ENTRY_NAME, true);
return;
}
this.faves_coop = Components.classes['@flock.com/singleton;1']
.getService(Components.interfaces.flockISingleton)
.getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
.wrappedJSObject;
this.account_root = this.faves_coop.accounts_root;
this.ytPersonRoot = new this.faves_coop.Folder("urn:youtube:peopleroot", {name: "YouTube People"});
this.ytService = new this.faves_coop.Service(
"urn:youtube:service",
{
name: "youtube",
desc: "The YouTube Service",
loginURL: gStrings["userlogin"],
contactLabel: 'Contacts'
}
);
this.ytService.serviceId = YOUTUBE_CONTRACTID;
var ytHomepage = new this.faves_coop.Favorite(
"urn:flock:youtube:actions:homepage", {
name: "youtube.com",
URL: "http://www.youtube.com"
});
var ytOpenProfile = new this.faves_coop.Action(
"urn:flock:youtube:actions:openprofile", {
name: "Open Profile",
method: "openProfile",
service: YOUTUBE_CONTRACTID,
flavour: "view"
});
var ytMySubs = new this.faves_coop.Action(
"urn:flock:youtube:actions:mysubscriptions", {
name: "Open Subscriptions",
method: "openSubscriptions",
service: YOUTUBE_CONTRACTID,
flavour: "accountview"
});
var ytMyInbox = new this.faves_coop.Action(
"urn:flock:youtube:actions:myinbox", {
name: "Open Inbox",
method: "openInbox",
service: YOUTUBE_CONTRACTID,
flavour: "accountview"
});
var ytMyVideos = new this.faves_coop.Action(
"urn:flock:youtube:actions:myvideos", {
name: "Open Videos",
method: "openVideos",
service: YOUTUBE_CONTRACTID,
flavour: "view"
});
var ytMyFavoriteVideos = new this.faves_coop.Action(
"urn:flock:youtube:actions:myfavvideos", {
name: "Open Favorite Videos",
method: "openFavVideos",
service: YOUTUBE_CONTRACTID,
flavour: "view",
isPrimary: true
});
var ytPostComment = new this.faves_coop.Action(
"urn:flock:youtube:actions:postcomment", {
name: "Post Comment",
method: "postComment",
service: YOUTUBE_CONTRACTID,
flavour: "contact"
});
var ytEditProfile = new this.faves_coop.Action(
"urn:flock:youtube:actions:editprofile", {
name: "Edit Profile",
method: "editProfile",
service: YOUTUBE_CONTRACTID,
flavour: "accountaction"
});
var ytViewFriends = new this.faves_coop.Action(
"urn:flock:youtube:actions:viewFriends", {
name: "View Contacts",
method: "viewFriends",
service: YOUTUBE_CONTRACTID,
flavour: 'view'
});
this.ytService.children.addOnce(ytViewFriends);
this.ytService.children.addOnce(ytHomepage);
this.ytService.children.addOnce(ytOpenProfile);
this.ytService.children.addOnce(ytMySubs);
this.ytService.children.addOnce(ytMyInbox);
this.ytService.children.addOnce(ytMyVideos);
this.ytService.children.addOnce(ytMyFavoriteVideos);
this.ytService.children.addOnce(ytPostComment);
this.ytService.children.addOnce(ytEditProfile);
// Load Web Detective file
this.webDetective = this.acUtils.useWebDetective("youtube.xml");
for (var s in gStrings) {
gStrings[s] = this.webDetective.getString("youtube", s, gStrings[s]);
}
this.ytService.domains = gStrings["domains"];
this.urn = this.ytService.id();
var accounts = this.faves_coop.Account.find({serviceId: YOUTUBE_CONTRACTID});
if (accounts.length) {
this.USER = accounts[0].accountId;
for (var i = 0; i < accounts.length; i++) {
this.updateActions(accounts[i].id());
}
}
this._profiler.profileEventEnd(evtID, "");
}
youtubeService.prototype.getError =
function youtubeService_getError (aErrorType, aXML, aHTTPErrorCode) {
var error = Components.classes["@flock.com/error;1"].createInstance(Ci.flockIError);
if (aErrorType == "HTTP_ERROR") {
error.errorCode = aHTTPErrorCode;
} else if (aErrorType == "SERVICE_ERROR") {
var errorCode;
var errorMessage;
var serviceErrorMessage;
try {
errorCode = aXML.getElementsByTagName("error")[0].getAttribute('code');
serviceErrorMessage = aXML.getElementsByTagName("error")[0].getAttribute('description');
} catch (ex) {
errorCode = "999" // in case the error xml is invalid
}
switch (errorCode) { // http://www.youtube.com/dev_error_codes
case "1":
error.errorCode = error.HTTP_INTERNAL_SERVER_ERROR;
break;
case "2": // These errors are due to Flock sending a bad request
case "3":
case "4":
case "5":
case "6":
error.errorCode = error.PHOTOSERVICE_INVALID_QUERY;
break;
case "7":
case "8":
error.errorCode = error.PHOTOSERVICE_INVALID_API_KEY;
break;
case "999":
error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
break;
default:
error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
break;
}
}
error.serviceErrorCode = errorCode;
error.serviceErrorString = serviceErrorMessage;
this._logger.error(error.errorString);
return error;
};
// BEGIN flockIPhotoAPI interface
youtubeService.prototype.__defineGetter__('authState', function () { return this.state; })
youtubeService.prototype.__defineGetter__('firstRunPerson', function () { throw "NotImplemented"; })
youtubeService.prototype.iconUrl = YOUTUBE_FAVICON;
youtubeService.prototype.__defineGetter__('isLoggedIn', function () { return this.api.isLoggedIn; })
youtubeService.prototype.__defineGetter__('isUploading', function () { return this.running; })
youtubeService.prototype.serviceName = "YouTube";
youtubeService.prototype.shortName = "youtube";
youtubeService.prototype.authenticatedCall = function(aListener, aMethod, aParams) {
throw "NotImplemented";
}
var channels = {
'special:added': {
title: 'Recently Added',
supportsSearch: false,
feed: 'http://youtube.com/rss/global/recently_added.rss'
},
'special:featured': {
title: 'Recently Featured',
supportsSearch: false
},
'special:top_favorites': {
title: 'Top Favorites',
supportsSearch: false,
feed: 'http://youtube.com/rss/global/top_favorites.rss'
},
'special:rated': {
title: 'Top Rated',
supportsSearch: false,
feed: 'http://youtube.com/rss/global/top_rated.rss'
}
}
youtubeService.prototype.supportsSearch =
function youtubeService_supportsSearch( aQueryString ) {
var aQuery = new queryHelper(aQueryString);
if (aQuery.special) {
var channel = channels["special:" + aQuery.special];
if (channel) {
return channel.supportsSearch;
}
}
// none of the other apis/feeds support search
return false;
}
youtubeService.prototype.getChannel =
function youtubeService_getChannel(aChannelId)
{
if (!(aChannelId in channels)) return null;
var nc = Components.classes['@flock.com/media-channel;1']
.createInstance(Components.interfaces.flockIMediaChannel);
nc.id = aChannelId;
nc.title = channels[aChannelId].title
nc.supportsSearch = channels[aChannelId].supportsSearch;
return nc;
}
youtubeService.prototype.__defineGetter__('channels', function () {
var ar = [];
for (var id in channels) {
var nc = Components.classes['@flock.com/media-channel;1']
.createInstance(Components.interfaces.flockIMediaChannel);
nc.id = id;
nc.title = channels[id].title
nc.supportsSearch = channels[id].supportsSearch;
ar.push(nc);
}
var rval = {
getNext: function() {
var rval = ar.shift();
return rval;
},
hasMoreElements: function() {
return (ar.length > 0);
}
}
return rval;
})
youtubeService.prototype.call =
function youtubeService_call(aListener, aMethod, aParams)
{
var url = "http://www.youtube.com/api2_rest?method=" + aMethod + "&dev_id=" + YT_DEVID;
for (p in aParams) {
url += "&" + p + "=" + aParams[p];
}
var hr = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1']
.createInstance(Components.interfaces.nsIXMLHttpRequest)
.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
var inst = this;
hr.onreadystatechange = function (aEvt) {
if (hr.readyState == 4) {
try {
if (hr.status/100 == 2) {
var rsp = hr.responseXML.getElementsByTagName("ut_response")[0];
var stat = rsp.getAttribute("status");
if (stat != "ok") {
var error = inst.getError('SERVICE_ERROR', hr.responseXML, null);
aListener.onError(error);
}
else {
aListener.onResult(hr.responseXML);
}
}
else {
// http errors
aListener.onError(inst.getError("HTTP_ERROR", null, hr.status));
}
} catch(e) {
// XMHTTPERROR (connection lost)
inst._logger.error(e);
aListener.onError(inst.getError("HTTP_ERROR", null, "9001"));
}
}
};
hr.detachLoadGroup = true;
hr.open('GET', url,true);
hr.send(null);
}
youtubeService.prototype.getFeed =
function youtubeService_getFeed(aListener, aFeedURL)
{
ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(aFeedURL, null, null);
var ytServ = this;
var feedListener = {
onGetFeedComplete: function oncomplete (feed) {
ytServ.handleFeedResult(aListener, feed);
},
onError: aListener.onError
};
var fm = Components.classes["@flock.com/feed-manager;1"]
.getService(Components.interfaces.flockIFeedManager);
fm.getFeed(uri, feedListener);
}
youtubeService.prototype.createAlbum =
function youtubeService_createAlbum(aListener, aAlbumName)
{
throw "NotImplemented";
}
youtubeService.prototype.findByUsername =
function youtubeService_findByUsername(aListener, aUsername)
{
var inst = this;
var myListener = {
onResult: function (aXML) {
var newUserObj = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID]
.createInstance(Components.interfaces.flockIPhotoPerson);
newUserObj.service = inst;
newUserObj.id = aUsername;
newUserObj.username = aUsername;
newUserObj.fullname = aUsername;
aListener.onFindByUsernameResult(newUserObj);
},
onError: function (aXML) {
aListener.onError(aXML);
}
}
var params = {};
params.user = aUsername;
this.call(myListener, "youtube.users.get_profile", params);
}
youtubeService.prototype.getAlbums = function(aListener, aUsername) { throw "NotImplemented"; }
youtubeService.prototype.getAuthPerson = function() {
if (!this.user) return null;
var newUserObj = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID]
.createInstance(Components.interfaces.flockIPhotoPerson);
newUserObj.id = this.api.user.username
newUserObj.username = this.api.user.username;
newUserObj.fullname = this.api.user.username;
newUserObj.service = this;
return newUserObj;
}
youtubeService.prototype.getContacts = function(aListener) { throw "NotImplemented"; }
youtubeService.prototype.getMostRecentPhotoForList = function(aListener, aEnumerator) { throw "NotImplemented"; }
youtubeService.prototype.getPhoto = function(aListener, aPhotoID) { throw "NotImplemented"; }
youtubeService.prototype.getValidPerson = function(aListener, aURL) { throw "NotImplemented"; }
youtubeService.prototype.login = function(aAccountURN, aListener) { throw "NotImplemented"; }
youtubeService.prototype.queryChannel =
function youtubeService_queryChannel(aListener, aQueryString, aCount, aPage)
{
var aQuery = new queryHelper(aQueryString);
// return; // XXX TODO FIXME: This is killing perf
var inst = this;
var myListener = {
onResult: function (aXML) {
var rval = inst.handlePhotosResult(aXML);
var enum_ = {
hasMoreElements: function() {
return (rval.length > 0);
},
getNext: function() {
return rval.shift();
}
}
aListener.onSearchResult(enum_);
},
onError: function (aError) {
aListener.onError(aError);
}
}
var params = {}
if (aQuery.search) {
params.tag = aQuery.search;
params.page = aPage;
params.per_page = aCount;
this.call(myListener, 'youtube.videos.list_by_tag', params);
} else {
// this API call doesn't support pagination and only shows most recent 25 items
if (aPage > 1) return;
var channel = channels[aQuery.stringVal];
if (!channel) return;
if (channel.feed)
this.getFeed(aListener, channel.feed);
else
this.call(myListener, "youtube.videos.list_featured", params);
}
}
youtubeService.prototype.search =
function youtubeService_search( aListener, aQueryString, aCount, aPage )
{
var aQuery = new queryHelper(aQueryString);
if (aPage > 1) return; // youtube doesn't support pagination
if(aQuery.favorites && !aQuery.user)
{
aQuery.user = aQuery.favorites;
}
if (!aQuery.user && aQuery.special != "favorites") {
this.queryChannel( aListener, aQueryString, aCount, aPage );
return;
}
var aUserid = aQuery.user;
var params = {
user: aUserid
};
var inst = this;
var myListener = {
onResult: function (aXML) {
var rval = inst.handlePhotosResult(aXML, aUserid);
var enum_ = {
hasMoreElements: function() {
return (rval.length > 0);
},
getNext: function() {
return rval.shift();
}
}
aListener.onSearchResult(enum_);
},
onError: function (aError) {
aListener.onError(aError);
}
}
if (aQuery.favorites) {
this.call(myListener, "youtube.users.list_favorite_videos", params);
} else {
this.call(myListener, "youtube.videos.list_by_user", params);
}
}
youtubeService.prototype.getPhotoFromRDFNode =
function (aRDFId)
{
var newPhoto = new youtubeVideo();
var coopPhoto = this.faves_coop.get(aRDFId);
newPhoto.webPageUrl = coopPhoto.URL;
newPhoto.thumbnail = coopPhoto.thumbnail;
newPhoto.midSizePhoto = coopPhoto.midSizePhoto;
newPhoto.largeSizePhoto = coopPhoto.largeSizePhoto;
newPhoto.username = coopPhoto.username;
newPhoto.userid = coopPhoto.userid;
newPhoto.title = coopPhoto.name;
newPhoto.id = coopPhoto.photoid;
newPhoto.icon = coopPhoto.favicon;
newPhoto.uploadDate = coopPhoto.datevalue;
newPhoto.is_public = coopPhoto.is_public;
newPhoto.is_video = "true";
return newPhoto;
// JMC XXX TODO: Gotta add the video-specific fields into the RDF, or somewhere
// eg content length, rating, etc.
}
youtubeService.prototype.handlePhotosResult =
function youtubeService_handlePhotoResult(aXML, aUserid)
{
var rval = [];
var photoList = aXML.getElementsByTagName("video");
for (var i = 0; i < photoList.length; i++) {
var photo = photoList[i];
var newPhoto = new youtubeVideo();
for (var j = 0; j < photo.childNodes.length; j++)
{
var child = photo.childNodes[j];
var contentNode = child.childNodes[0];
var childContent = "";
if (contentNode) childContent = contentNode.nodeValue;
switch (child.tagName)
{
case "author":
newPhoto.username = childContent;
newPhoto.userid = childContent;
break;
// case "id": newPhoto.id = childContent; break;
case "title": newPhoto.title = childContent; break;
case "upload_time":
newPhoto.uploadDate = childContent*1000;
newPhoto.id = parseInt(childContent);
break;
case "length_seconds":
newPhoto.length_seconds = childContent;
break
case "rating_avg":
newPhoto.rating_avg = childContent;
break
case "description":
newPhoto.description = childContent;
break
case "comment_count":
newPhoto.comment_count = childContent;
break
case "view_count":
newPhoto.view_count = childContent;
break
case "rating_count":
newPhoto.rating_count = childContent;
break
case "url":
newPhoto.webPageUrl = childContent;
break;
case "thumbnail_url":
newPhoto.thumbnail = childContent;
newPhoto.midSizePhoto = childContent;
newPhoto.largeSizePhoto = childContent;
break;
// case "id": newPhoto.id = childContent;
// break;
}
}
newPhoto.is_public = "true";
newPhoto.is_video = true;
rval.push(newPhoto);
}
return rval;
}
youtubeService.prototype.handlePeopleResult =
function youtubeService_handlePeopleResult(aXML, aUserid)
{
var rval = [];
var friendList = aXML.getElementsByTagName("friend");
for (var i = 0; i < friendList.length; i++) {
var person = friendList[i];
var newPerson = {};
for (var j = 0; j < person.childNodes.length; j++)
{
var child = person.childNodes[j];
var contentNode = child.childNodes[0];
var childContent = "";
if (contentNode) childContent = contentNode.nodeValue;
switch (child.tagName)
{
case "user":
newPerson.name = childContent;
newPerson.accountId = childContent;
break;
/*
case "friend_count": newPerson.friend_count = childContent; break;
case "video_upload_count":
newPerson.videoCount = childContent;
break;
*/
}
}
rval.push(newPerson);
}
return rval;
}
youtubeService.prototype.handleFeedResult =
function youtubeService_handleFeedResult(aListener, aFeed)
{
var photos = [];
var items = aFeed.getItems();
while (items && items.hasMoreElements()) {
var item = items.getNext();
var newPhoto = new youtubeVideo();
newPhoto.title = item.getTitle();
newPhoto.webPageUrl = item.getLink().spec;
newPhoto.uploadDate = item.getPubDate();
newPhoto.id = newPhoto.uploadDate / 1000;
var author = item.getAuthor();
newPhoto.username = author;
newPhoto.userid = author;
var desc = item.getContent();
var re = /<img [^>]*src="([^"]*)"/i;
var thumbnail = desc.match(re)[1];
newPhoto.thumbnail = thumbnail;
newPhoto.midSizePhoto = thumbnail;
newPhoto.largeSizePhoto = thumbnail;
newPhoto.is_public = "true";
newPhoto.is_video = true;
photos.push(newPhoto);
}
var enum_ = {
hasMoreElements: function() {
return (photos.length > 0);
},
getNext: function() {
return photos.shift();
}
}
aListener.onSearchResult(enum_);
}
youtubeService.prototype.supportsFeature = function(aFeature) {
var supports = {};
supports.tags = true;
supports.title = true;
supports.fileName = false;
supports.contacts = true;
supports.privacy = false;
supports.albumCreation = false;
return (supports[aFeature] == true);
}
youtubeService.prototype.upload = function(aListener, aFile, aParams, aUpload) { throw "NotImplemented"; }
youtubeService.prototype.upload2 = function(aListener, aUpload, aFilename) { throw "NotImplemented"; }
function dictionary2Params(aDictionary) {
var params = {};
var obj = {};
var count = {};
var keys = aDictionary.getKeys(count, obj);
for (var i = 0; i < keys.length; ++i) {
var supports = aDictionary.getValue(keys[i]);
var supportsString = supports.QueryInterface(Components.interfaces.nsISupportsString);
var val = supportsString.toString();
params[keys[i]] = val;
}
return params;
}
youtubeService.prototype.refresh =
function youtubeService_refresh(aURN, aListener)
{
DEBUG("refresh with aURN of " + aURN);
var refreshItem = this.faves_coop.get(aURN);
if (refreshItem.isInstanceOf(this.faves_coop.Account)) {
this.refreshAccount(aURN, aListener);
} else if (refreshItem.isInstanceOf(this.faves_coop.Favorite)) {
this.refreshItem(aURN, aListener);
} else if (refreshItem.isInstanceOf(this.faves_coop.MediaQuery)) {
// TODO refresh MediaQueries?
} else {
throw Components.results.NS_ERROR_ABORT;
}
}
youtubeService.prototype.refreshItem =
function youtubeService_refreshItem(aURN, aListener)
{
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
youtubeService.prototype.refreshAccount =
function youtubeService_refreshAccount(aURN, aListener)
{
if (!aURN) return;
var refreshItem = this.faves_coop.get(aURN);
this.getFriends(aListener, refreshItem);
}
// JMC - Convenience wrapper, probably overkill
youtubeService.prototype.addFoafPerson =
function youtubeService_addFoafPerson(aPhotoPerson, aFriendOfURN)
{
}
// JMC - XXX TODO - Need to sanity the diff between currAcct and aAccount in all
// these methods, make sure it makes sense
youtubeService.prototype.addCoopPerson =
function youtubeService_addCoopPerson(aPhotoPerson, bIsTransient, aFriendOfURN)
{
}
youtubeService.prototype.addMediaStream =
function youtubeService_addMediaStream(aFriend, aCoopAccount)
{
var query = new queryHelper();
query.user = aFriend.accountId;
query.username = aFriend.accountId;
var mediaqueryURN = "urn:media:favorites:youtube:"+query.stringVal;
var mediaquery = this.faves_coop.get(mediaqueryURN);
if (!mediaquery) {
mediaquery = new this.faves_coop.MediaQuery(
mediaqueryURN,
{
serviceId: PHOTOAPIMGR_CONTRACTID,
service: this.shortName,
favicon: YOUTUBE_FAVICON
}
);
}
mediaquery.query = query.stringVal;
mediaquery.name = aFriend.name;
mediaquery.isPollable = true;
mediaquery.isTransient = aCoopAccount.isTransient;
var mediaFavesURN = "urn:media:favorites";
var mediaFaves = this.faves_coop.get(mediaFavesURN);
if (!mediaFaves) {
mediaFaves = new this.faves_coop.Folder(mediaFavesURN);
this.faves_coop.favorites_root.children.add(mediaFaves);
}
mediaFaves.children.addOnce(mediaquery);
}
youtubeService.prototype.handleFriendsResult =
function youtubeService_handleFriendsResult(aXML, aAccount)
{
DEBUG(".handleFriendsResult(aXML, aAccount)");
var friendList = aXML.getElementsByTagName("friend");
DEBUG(" - found "+friendList.length+" friends");
for (var i = 0; i < friendList.length; i++) {
var friend = friendList[i];
var friendObj = {};
for (var j = 0; j < friend.childNodes.length; j++)
{
var child = friend.childNodes[j];
var contentNode = child.childNodes[0];
var childContent = "";
if (contentNode) childContent = contentNode.nodeValue;
switch (child.tagName)
{
case "user":
{
friendObj.accountId = childContent;
friendObj.name = childContent;
}; break;
}
}
//this.addCoopPerson(friendObj);
this.addMediaStream(friendObj, aAccount);
}
}
youtubeService.prototype.getFriends =
function youtubeService_getFriends(aListener, aAccount)
{
var inst = this;
var myListener = {
onResult: function (aXML) {
inst.handleFriendsResult(aXML, aAccount);
aListener.onResult();
},
onError: function (aXML) {
aListener.onError();
}
}
var params = {};
params.user = aAccount.accountId;
this.call(myListener, "youtube.users.list_friends", params);
}
var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://browser/content/utilityOverlay.js");
youtubeService.prototype.doAction =
function youtubeService_doAction(aMethodName, aURN, aSubject)
{
var actionTarget = this.faves_coop.get(aURN);
switch (aMethodName)
{
case "openProfile":
return gStrings["userprofile"].replace("%accountid%", actionTarget.accountId);
break;
case "openSubscriptions":
return gStrings["subscriptions"];
break;
case "openVideos":
// return "http://youtube.com/my_videos";
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow('navigator:browser');
if (win) {
win.gPhotoDrawer.loadPhotoPerson(aURN, this.shortName);
}
break;
case "openFavVideos":
// return "http://youtube.com/my_favorites";
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow('navigator:browser');
if (win) {
win.gPhotoDrawer.loadQuery(this.shortName, "favorites:" + actionTarget.accountId, actionTarget.name);
}
break;
case "openInbox":
return gStrings["inbox"];
break;
case "editProfile":
return gStrings["editprofile"];
break;
case "postComment":
return actionTarget.URL + "?mode=reply";
break;
case "viewFriends":
// Dangerous assumption that the people sidebar is open when this is called
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow('navigator:browser');
if (win) {
var peepsSidebar = win.document.getElementById('sidebar').contentWindow;
if (peepsSidebar)
peepsSidebar.gPeopleBrowser.getCurrent(actionTarget.id());
}
break;
default:
dump("BORK BORK - undefined action in youtube\n");
break;
}
return null;
}
youtubeService.prototype.updateActions =
function youtubeService_updateActions(aURN)
{
var coopObj = this.faves_coop.get(aURN);
if (coopObj.isInstanceOf(this.faves_coop.Account)) {
coopObj.enabledAction.removeAll();
var serviceActions = this.ytService.children.enumerate();
while (serviceActions.hasMoreElements()) {
var action = serviceActions.getNext();
if (action.flavour == "accountaction" ||
action.flavour == "accountview" ||
action.flavour == "view")
{
coopObj.enabledAction.add(action);
}
}
}
}
// BEGIN flockIWebService inteface
youtubeService.prototype.addAccountById =
function youtubeService_addAccountById(aAccountID, aIsTransient, aListener)
{
DEBUG("{flockIWebService}.addAccountById('"+aAccountID+"', "+aIsTransient+", aListener)");
var accountURN = "urn:flock:youtube:"+aAccountID;
var account = new this.faves_coop.Account(
accountURN,
{
name: aAccountID,
serviceId: YOUTUBE_CONTRACTID,
service: this.ytService,
accountId: aAccountID,
URL: gStrings["userprofile"].replace("%accountid%", aAccountID),
isTransient: aIsTransient,
favicon: YOUTUBE_FAVICON
}
);
this.account_root.children.add(account);
this.USER = account.id();
var notifUrn = accountURN + ":notifications";
var notifications = new this.faves_coop.Stream(
notifUrn,
{
name: "YouTube Notifications",
isPollable: false,
isIndexable: false,
notify: true,
serviceId: YOUTUBE_CONTRACTID
}
);
account.children.addOnce(notifications);
// Instanciate account component
var acct = this.getAccount(account.id());
if (aListener) aListener.onSuccess(acct, "addAccount");
return acct;
}
// END flockIWebService interface
// BEGIN flockIManageableWebService interface
youtubeService.prototype.updateAccountStatusFromDocument =
function youtubeService_updateAccountStatusFromDocument(aDocument)
{
DEBUG("{flockIManageableWebService}.updateAccountStatusFromDocument(aDocument)");
if (this.docRepresentsSuccessfulLogin(aDocument)) {
var accountID = this.getAccountIDFromDocument(aDocument);
var acctURN = this.acUtils.getAccountURNById(this.urn, accountID);
var accounts = this.faves_coop.Account.find({serviceId: YOUTUBE_CONTRACTID});
for (var i = 0; i < accounts.length; i++) {
if (accounts[i].id() == acctURN) {
accounts[i].isAuthenticated = true;
this.USER = acctURN;
} else {
accounts[i].isAuthenticated = false;
}
}
} else {
this.acUtils.markAllAccountsAsLoggedOut(YOUTUBE_CONTRACTID);
}
}
// END flockIManageableWebService interface
// BEGIN flockISocialWebService interface
youtubeService.prototype.decorateForPerson =
function youtubeService_decorateForPerson(aDocument)
{
DEBUG("{flockISocialWebService}.decorateForPerson()");
}
youtubeService.prototype.browseFriends =
function youtubeService_browseFriends(aFriendURN, aListener)
{
DEBUG("{flockISocialWebService}.browseFriends('"+aFriendURN+"')");
}
// END flockISocialWebService interface
youtubeService.prototype.getFriendsContacts =
function youtubeService_getFriendsContacts(aListener, aUsername)
{
var inst = this;
var myListener = {
onResult: function (aXML) {
var rval = inst.handlePeopleResult(aXML);
var enum_ = {
hasMoreElements: function() {
return (rval.length > 0);
},
getNext: function() {
return rval.shift();
}
}
aListener.onGetContactsResult(enum_);
},
onError: function (aXML) {
aListener.onError(aXML);
}
}
var params = {};
params.user = aUsername;
this.call(myListener, "youtube.users.list_friends", params);
}
// BEGIN flockIMediaWebService interface
youtubeService.prototype.decorateForMedia =
function youtubeService_decorateForMedia(aDocument)
{
DEBUG("{flockIMediaWebService}.decorateForMedia(aDocument)");
aDocument.QueryInterface(Components.interfaces.nsIDOMHTMLDocument);
var results = Components.classes["@mozilla.org/hash-property-bag;1"]
.createInstance(Components.interfaces.nsIWritablePropertyBag2);
if (this.webDetective.detect("youtube", "media", aDocument, results)) {
var mediaArr = [];
// media item for user videos
var userid = results.getPropertyAsAString("userid");
var media = {
name: userid,
query: 'user:' + userid + "|username:" + userid,
label: userid + "'s Videos", // FIXME: breaks internationalization
favicon: this.icon,
service: this.shortName
}
mediaArr.push(media);
// media item for user favorites
var media = {
name: userid,
query: 'favorites:' + userid,
label: userid + "'s Favorites", // FIXME: breaks internationalization
favicon: this.icon,
service: this.shortName
}
mediaArr.push(media);
if (!aDocument._flock_decorations) {
aDocument._flock_decorations = {};
}
aDocument._flock_decorations.mediaArr = mediaArr;
this.obs.notifyObservers(aDocument, 'media', 'media:update');
}
}
youtubeService.prototype.handlesMediaStream =
function youtubeService_handlesMediaStream()
{
return true;
}
youtubeService.prototype.checkIsStreamUrl =
function youtubeService_checkIsStreamUrl(aUrl)
{
this._logger.debug("Checking Stream Url for Youtube: " + aUrl);
if (aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/v\/.*/) ||
aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/watch\/v\/.*/) ||
aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/watch\?v=.*/) ||
aUrl.match(/^\/player\w*\.swf\?v*/) ) {
return true;
}
return false;
}
youtubeService.prototype.getVideoIDFromUrl =
function youTubeService_getVideoIDFromUrl(aUrl)
{
// These are image link matches
//http://www.youtube.com/v/lp_daXRkOH0
userMatch = aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/v\/(.*)/);
if (userMatch) {
return userMatch[1];
}
//http://www.youtube.com/watch/v/YP2rgi978tw
userMatch = aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/watch\/v\/(.*)/);
if (userMatch) {
return userMatch[1];
}
//http://www.youtube.com/watch?v=BjfbS_Kj-J0
userMatch = aUrl.match(/^https?:\/\/[^\/]*\.?youtube.com\/watch\?v=(.*)/);
if (userMatch) {
return userMatch[1];
}
// These are flash video sources matching
// /player2.swf?video_id=xPxDw7ajfGE&....
var userMatch = aUrl.match(/^\/player2\.swf\?.*video_id=([^&]+).*/);
if (userMatch){
return userMatch[1];
}
return null;
}
youtubeService.prototype.getMediaQueryFromURL =
function youTubeService_getMediaQueryFromURL(aUrl, aListener)
{
var videoId = this.getVideoIDFromUrl(aUrl);
if (videoId) {
var myListener = {
onResult: function (aXML) {
var userID = aXML.getElementsByTagName('author')[0].firstChild.nodeValue;
var results = Components.classes["@mozilla.org/hash-property-bag;1"]
.createInstance(Components.interfaces.nsIWritablePropertyBag2);
results.setPropertyAsAString("query", "user:" + userID + "|username:" + userID);
results.setPropertyAsAString("title", this.title + " user: " + userID);
aListener.onSuccess(results, "query");
},
onError: function (aError) {
aListener.onError(null, aError, null);
}
}
var params = {};
params.video_id = videoId;
this.call(myListener, "youtube.videos.get_details", params);
} else {
aListener.onError(null, "Unable to get user.", null);
}
}
// END flockIMediaWebService interface
// ========== END youtubeService class ==========
// ================================================
// ========== BEGIN youtubeAccount class ==========
// ================================================
function youtubeAccount()
{
this.acUtils = Components.classes["@flock.com/account-utils;1"]
.getService(Components.interfaces.flockIAccountUtils);
this.service = Components.classes[YOUTUBE_CONTRACTID]
.getService(Components.interfaces.flockIWebService)
.QueryInterface(Components.interfaces.flockISocialWebService);
this._coop = Components.classes["@flock.com/singleton;1"]
.getService(Components.interfaces.flockISingleton)
.getSingleton("chrome://browser/content/flock/common/load-faves-coop.js")
.wrappedJSObject;
this._ctk = {
interfaces: [
"nsISupports",
"flockIWebServiceAccount",
"flockIMediaWebServiceAccount",
"flockISocialWebServiceAccount",
]
};
getCompTK().addAllInterfaces(this);
}
// BEGIN flockIWebServiceAccount interface
youtubeAccount.prototype.urn = "";
youtubeAccount.prototype.username = "";
youtubeAccount.prototype.status = "";
youtubeAccount.prototype.activate =
function youtubeAccount_activate(aListener)
{
DEBUG("{flockIWebServiceAccount}.activate()");
var acctCoopObj = this._coop.get(this.urn);
acctCoopObj.isPollable = true;
if (aListener) {
aListener.onSuccess(this, "accountAuthorized");
}
}
youtubeAccount.prototype.login =
function youtubeAccount_login(aListener)
{
DEBUG("{flockIWebServiceAccount}.login()");
}
// END flockIWebServiceAccount interface
// BEGIN flockISocialWebServiceAccount interface
youtubeAccount.prototype.browseFriends =
function youtubeAccount_browseFriends(aFriendURN, aListener)
{
DEBUG("{flockISocialWebServiceAccount}.browseFriends('"+aFriendURN+"')");
this.service.browseFriends(aFriendURN, aListener);
}
// END flockISocialWebServiceAccount interface
// ========== END youtubeAccount class ==========
// ==============================================
// ========== BEGIN XPCOM registration ==========
// ==============================================
function createModule(aParams) {
return {
registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
aParams.contractID, aFileSpec,
aLocation, aType );
var catMgr = Cc["@mozilla.org/categorymanager;1"]
.getService(Ci.nsICategoryManager);
if (!aParams.categories) { aParams.categories = []; }
for (var i = 0; i < aParams.categories.length; i++) {
var cat = aParams.categories[i];
catMgr.addCategoryEntry( cat.category, cat.entry,
cat.value, true, true );
}
},
getClassObject: function (aCompMgr, aCID, aIID) {
if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
return { // Factory
createInstance: function (aOuter, aIID) {
if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
var comp = new aParams.componentClass();
if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
return comp.QueryInterface(aIID);
}
};
},
canUnload: function (aCompMgr) { return true; }
};
}
// NS Module entrypoint
function NSGetModule(aCompMgr, aFileSpec) {
return createModule({
componentClass: youtubeService,
CID: YOUTUBE_CID,
contractID: YOUTUBE_CONTRACTID,
componentName: CATEGORY_COMPONENT_NAME,
implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
categories: [
{ category: "wsm-startup", entry: CATEGORY_COMPONENT_NAME, value: YOUTUBE_CONTRACTID },
{ category: "flockIPhotoAPI", entry: CATEGORY_ENTRY_NAME, value: YOUTUBE_CONTRACTID },
{ category: "flockWebService", entry: CATEGORY_ENTRY_NAME, value: YOUTUBE_CONTRACTID }
]
});
}
// ========== END XPCOM registration ==========
// HELPER STUFF
function loadSubScript(spec)
{
var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
var context = {};
loader.loadSubScript(spec, context);
return context;
}
function loadLibraryFromSpec(aSpec)
{
var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
loader.loadSubScript(aSpec);
}